import { onBeforeUnmount, onMounted, ref } from 'vue';

import { asyncTimeout, to } from '@/utils/promise.utils';

interface Options<T> {
  manual?: boolean
  retryCount?: number
  manualPolling?: boolean
  pollingInterval?: number
  autoAbortPrevious?: boolean
  onBeforeRequest?: () => void
  onSuccess?: (data: T) => void
  onError?: (err: any) => void
  onComplete?: () => void
}
function isPromise(fetch: any): fetch is Promise<any> {
  return fetch instanceof Promise;
}

export function useRequest<T = Props>(fetch: Promise<T> | (() => Promise<T>), options: Options<T> = {
  manual: false,
  manualPolling: false,
  autoAbortPrevious: true
}) {
  options = Object.assign(
    {
      manual: false,
      manualPolling: false,
      autoAbortPrevious: true
    },
    options
  );
  const loading = ref(false);
  const data = ref<T>();

  const fetchTask: ((reason?: any) => void)[] = [];

  // 请求主体
  const runAsync = async (retryCount?: number): Promise<T> => {
    try {
      try {
        if (options.autoAbortPrevious) {
          await abort();
        }
        loading.value = true;
        options.onBeforeRequest && options.onBeforeRequest();
        // 中断promise
        const abortRequest = (): Promise<void> => {
          return new Promise((resolve, reject) => {
            fetchTask.push(reject);
          });
        };
        const fetchExecute = isPromise(fetch) ? fetch : fetch();
        const [err, result] = await to(
          Promise.race([fetchExecute, abortRequest()])
        );
        if (!err && !isCancelTimer) {
          data.value = result as T;
          options.onSuccess && options.onSuccess(result as T);
          return result as T;
        }
        // data.value = undefined as T;
        options.onError && options.onError(err);
        return Promise.reject(err);
      }
      catch (err) {
        if (retryCount) {
          runAsync(--retryCount);
        }
        else {
          data.value = undefined as T;
          options.onError && options.onError(err);
          return Promise.reject(err);
        }
      }
    }
    finally {
      loading.value = false;
      options.onComplete && options.onComplete();
    }
    return Promise.reject(new Error(`请求失败:${fetch}`));
  };

  let timer: ReturnType<typeof setTimeout>;
  // 是否关闭定时器轮询
  let isCancelTimer = false;
  // 轮询
  const timeTask = async () => {
    clearTimeTask();
    isCancelTimer = false; // 重置
    timer = setTimeout(async () => {
      await to(runAsync(options.retryCount));
      if (!isCancelTimer) {
        timeTask();
      }
    }, options.pollingInterval);
  };
  // 清除定时轮询
  const clearTimeTask = () => {
    if (timer) {
      clearTimeout(timer);
    }
    timer = undefined;
    isCancelTimer = true;
  };

  // 中断请求
  const abort = () => {
    fetchTask.forEach(reject => {
      reject('cancel');
    });
    fetchTask.splice(0, fetchTask.length);
    return asyncTimeout();
  };
  onMounted(async () => {
    // 判断是否需要延迟执行
    if (!options.manual) {
      await runAsync(options.retryCount);
    }
    // 判断是否需要轮训
    if (!options.manualPolling && options.pollingInterval) {
      timeTask();
    }
  });
  onBeforeUnmount(() => {
    clearTimeTask();
  });

  return {
    loading,
    data,
    abort,
    runAsync,
    timeTask,
    clearTimeTask
  };
}
